دورة البايثون -الأستاذ القسيمي- ملخص كتابي للدرس السادس : القوائم و الحلقات التكرارية for loop:
السلام عليكم و طابت أوقاتكم ، 
ملاحظات قبل البدء :
* هذا الملخص ليس درسا بذاته بل هو تلخيص كتابي للدورة التفاعلية للأستاذ سليمان القسيمي و لا يمكن الإعتماد عليه بمفرده بل وجب الإستئناس به لمراجعة الدورة و الإستماع لتسجيلاتها الصوتية .
* أنا أكتب الأكواد و أجربها من خلال محرر أكواد لذلك تجدونني دوما أستخدم الدالة print الأمر الذي يمكنكم الإستغناء عنه لو أنتم تستخدمون مفسر البايثون .
نبدأ درس اليوم بما تبقى لنا من درس السلاسل النصية :
الدالة count() ترجع لنا عدد المرات التي ورد فيها ما نضعه بين قوسيها :
txt = "أهلا بكم في هذه الليلة السعيدة"
txt = txt.count("ل")
print(txt)
و هذا الناتج :
5
لطباعة أمر ما إستخدمنا fstrings و هي ليست الطريقة الوحيدة بل توجد طرقا أخرى من بينها format و هي شبيهة بfstrings ، قد لا تحتاجونها ( على الأقل في هذه المرحلة) و لكن من واجبي أن أذكرها لكم لتأخذوا بها علما : 
x = 5
y = 6 
print("{} + {} = {}".format(x,y,x+y))
كما لاحظتم في النصف الأول وضعنا أقواس مزخرفة فارغة ثم في النصف الثاني وضعنا المتغيرات التي سنملأ بها الأقواس الفارغة بالترتيب و بالتتابع .
5 + 6 = 11

# القوائم :
و ننطلق في درس القوائم ، القوائم هي كائن يمكننا أن نخزن فيه أكثر من عنصر، رأينا في السلسلة النصية أن المتغير الذي نسنده إلى قطعة نصية لا يمكننا إضافة قطع أخرى أو عناصر أخرى مختلفة، أما القائمة فهي يمكنها أن تضم أكثر من عنصر بل و أكثر من ذلك أنها ممكن أن تضم عناصر مختلفة مثل نص -رقم صحيح-رقم عشري-قيمة منطقية، قائمة فرعية ،  ....إلخ ، و توضع هذه العناصر بين قوسين مربعين و نفصل بين عناصرها بالفاصلة .
و هذا مثال مبسط لقائمة :
test = ["ahmed","أحمد", 1,2,3.5,4.6,True,False]
print(test)
و هذه قائمتنا كما طبعها لنا البايثون على الشاشة :
['ahmed', 'أحمد', 1, 2, 3.5, 4.6, True, False]
الآن سنقوم بأنشاء قائمة و نسندها لمتغير ليسهل علينا التعامل معها و سنضع في هذه القائمة عدد من أسماء الأشخاص و سترافقنا هذه القائمة في ما تبقى من درسنا هذا .
members = ["ahmed","ali","khaled","mohammed"]
print(members)
print(type(members))
و هذا الناتج نجد محتويات القائمة و الصنف الذي تنتمي إليه و هو list :
["ahmed","ali","khaled","mohammed"]
<class 'list'>
القوائم هي التي تستخدم ، مثلا، في combo box and listbox و هي كائنات رسومية معروفة نجدها في واجهات البرامج و يمكننا التعمق فيها لو سمحت لنا الظروف بدورة أخرى متقدمة أو يمكنكم دراستها بمفردكم عندما تصبحون قادرين على برمجة واجهات رسومية. 
بعد أن عرفنا محتويات قائمتنا و صنفها ، يمكننا مثلا معرفة عدد عناصر قائمتنا هذه بالدالة التي عرفناها في درسنا السابق و هي len()
print(len(members))
 وهذا ما طبعه لنا البايثون :
4
هل أقنعكم البايثون عندما أخبرنا أن عدد عناصر القائمة هو 4 عناصر ، هل تصدقونه ؟ 
فلو أخذنا كلمة mohammed وحدها تتكون من 8 عناصر و ali تتكون من 3 عناصر ، فكيف نصدقه عندما يقول أن كل القائمة عدد عناصرها 4 .
و بمأ أننا إختلفنا مع البايثون في تحديد عدد عناصر القائمة ، الحل هو أن نلتجىء إلى حكم يفصل بيننا و بين البايثون .
و الحَكَم المتوفر هي قواعد البايثون : تقول لنا قاعدة البايثون أن كل قيمة في المتغيّر "قائمة" هو عنصر بغض النظر عن عدد عناصره التي تكونه كعنصر مثلا ك قطعة نصية ترمز لإسم أنسان أو حيوان أو نبات أو جماد هي كلها عنصر واحد بمقياس رشتر ، عفوا بمقياس قواعد القوائم في البايثون ، إذن صديقنا لم يكذب علينا بل صدقنا القول عندما قال لنا أن عدد عناصر قائمتنا هو 4 عناصر .
فهمنا من هذه القاعدة أن محمد عنصر و علي عنصر ...بينما في السلاسل النصية كل حرف من عبارة "محمد" هي عنصر ، هذا فرق بين القوائم و السلاسل النصية ، أما طريقة الوصول للعناصر داخل القائمة فهي نفسها التي إستخدمناها للوصول لعناصر السلاسل النصية ، بمعنى عندما نريد الوصول إلى عنصر محدد نستخدم index الخاص به أي ترتيبه داخل القائمة ، و أظن أنني لست بحاجة لتذكيركم أن ترتيب العناصر يبدأ من الصفر .
للوصول للعنصر mohammed نستخدم ترتيبه الذي هو 3 :
print(members[3])
العنصر محمد هو آخر عنصر في القائمة ، هل توجد طريقة أخرى للوصول إليه ؟ نعم :
print(members[-1])
mohammed
mohammed

يمكننا إقتطاع قائمة فرعية من قائمتنا ، كما يلي :
members1 = members[1:]
print(members1)
و هذا الناتج :
['ali', 'khaled', 'mohammed']
و يمكننا عكس القائمة بالطريقة التالية :
members2 = members[::-1]
print(members2)
و هذه هي النتيجة :
['mohammed', 'khaled', 'ali', 'ahmed']
يمكننا عكس القائمة بطريقة أخرى و هي إستخدام الدالة reverse كما يلي :
members.reverse() و الفرق بينهما أن الطريقة الأولى تنشأ لكم قائمة جديدة بينما الطريقة الثانية تعكس نفس القائمة .
القوائم لديها عدد من الدوالي تمنحنا سهولة في التعامل معها .
#الدالة append()تمكنكم من إضافة عنصر جديد إلى القائمة و تضعه append في آخر عنصر أي يصبح العنصر الجديد هو العنصر الأخير في القائمة و نضع هذا العنصر بين قوسي الدالة هكذا :
members.append("messaoud")
print(members)
و نرى هنا النتيجة فقد تمت إضافة صديقنا مسعود للقائمة :
['ahmed', 'ali', 'khaled', 'mohammed', 'messaoud']
و الدالة insert تضيف عنصرا جديدا للقائمة مع تحديد ترتيبه و هي تستقبل منا معاملين ألاول ترتيب العنصر الجديد في القائمة و الثاني هو العنصر الجديد نفسه و يفصل بين المعاملين فاصلة ، هكذا :
members.insert(0,"soulimen")
print(members)
النتيجة:
 ['soulimen', 'ahmed', 'ali', 'khaled', 'mohammed', 'messaoud']
و للتأكد أن insert هذه تضيف عنصرا في أي مكان نحدده و أن القائمة تقبل عنصرين بنفس القيمة في مواقعين مختلفين سأضيف عنصرا جديدا بنفس القيمة في الموقع الرابع من القائمة أي سيحتل الترتيب 3 .
members.insert(3,"soulimen")
print(members)
و هذه نتيجة تنفيذ هذا الأمر :
['soulimen', 'ahmed', 'ali', 'soulimen', 'khaled', 'mohammed', 'messaoud', 'alaa', 'salem', 'mohsen'] :
مع append و insert أمكننا إضافة عناصر للقائمة في آخرها أو في أي موقع نريده ، مع الدالة extend()سنرى كيف يمكننا إضافة قائمة كاملة لقائماتنا ، القائمة ستضاف كعناصر في آخر قائمتنا و لكن طريقة الإضافة تختلف عن appendفمع extendنضيف قائمة بطريقتين : الأولى هكذا [نضع هنا عناصر القائمة المراد إضافتها لقائمتنا الأساسية] أما الطريقة الثانية فهي إضافة القائمة بعد إسنادها لمتغيّر و إضافة المتغيّر للقائمة الرئيسية ، الكود التالي يوضح الطريقتين لو لم يكن شرحي هذا واضحا بالشكل الكافي :
#الطريقة 1 
members.extend(["alaa","salem","mohsen"])
print(members)
و هذه هي النتيجة
['soulimen', 'ahmed', 'ali', 'soulimen', 'khaled', 'mohammed', 'messaoud', 'alaa', 'salem', 'mohsen']
الطريقة2
members3 = ["leila","maryem","semia"]
members.extend(members3)
print(members)
و هذه نتيجة تنفيذ الكود و نلاحظ أن الطريقتين أوصلتنا لنفس النتيجة و هي إضافة عناصر للقائمة الرئيسية :
['soulimen', 'ahmed', 'ali', 'soulimen', 'khaled', 'mohammed', 'messaoud', 'alaa', 'salem', 'mohsen', 'leila', 'maryem', 'semia']
ما رأيكم لو نتثبت من تطور عملنا و نرى كم إمتدت قائمتنا التي إنطلقنا بها و هي تحتوي 4 عناصر ، فهيا بنا نرى كم أصبح عدد عناصرها ، هل تذكرون كيف نتثبت من ذلك ؟ نعم أكيد تذكرون و إن نسيتم أذكركم أن معرفة عدد عناصر القائمة يتم بالدالة len() :
print(len(members))
و هذا هو عدد عناصر قائمتنا الحالي أتيتكم بها من شاشة الكمبيوتر بعد تنفيذ هذا السطر البرمجي 
13
مثلما مكننا البايثون من إضافة عناصر للقائمة بطرق مختلفة فهو لم يُقصّر معنا و أمدنا بأدوات تمكننا من حذف عناصر معينة .
الحذف يتم على أساس قيمة العنصر ، مثال :
members.remove("soulimen")
print(members)
و هذه هي النتيجة و تلاحظون أن العنصر سليمان الذي كان يحتل المرتبة صفر قد إختفى إذ تم حذفه للأن هذه الدالة removeتمر على القائمة عنصرا عنصرا و عندما تلتقي بالعنصر الذي وضعنا لها قيمته بين قوسيها فإنها تحذفه و تتوقف عن العمل و ترجع لنا نتيجة عملها أي القائمة دون العنصر الذي طلبنا منها حذفه و إذا كان هذا العنصر متكررا في القائمة فإنها تحذف العنصر ذي الترتيب الأدنى .
['ahmed', 'ali', 'soulimen', 'khaled', 'mohammed', 'messaoud', 'alaa', 'salem', 'mohsen', 'leila', 'maryem', 'semia']
و يمنحنا البايثون الغالي على قلوبنا طريقة ثانية للحذف و هي ترتكز على ترتيب العنصر في القائمة و ليس على قيمته .
members.pop(2)
print(members)
و هذه هي النتيجة و تلاحظون أن سليمان الذي كان يحتل الموقع الثالث ( الترتيب 2) قد تمّ حذفه .
['ahmed', 'ali', 'khaled', 'mohammed', 'messaoud', 'alaa', 'salem', 'mohsen', 'leila', 'maryem', 'semia']
كما يمكننا التعديل على أي عنصر ، بتحديد ترتيبه في القائمة، مثال :
members[0] = "mustapha"
قلنا للبايثون عدّل العنصر الذي يحتل المرتبة صفر و الذي أصبح في الأسطر السابقة ahmedبعد أن حذفنا العنصر سليمان الذي كان هو المحتل للمرتبة صفر ، قلنا للبايثون عدّل العنصر ذي المرتبة صفر و عوّضه بمصطفى و هذه هي النتيجة :
print(members)
['mustapha', 'ali', 'khaled', 'mohammed', 'messaoud', 'alaa', 'salem', 'mohsen', 'leila', 'maryem', 'semia']
و لدينا الدالة count أيضا التي تقوم بحساب عدد العناصر من قيمة معينة :
c = members.count("mustapha")
print(c)
و يعلمنا البايثون أن عبارة mustaphaوردت مرة واحدة في القائمة .
و لدينا كذلك الدالة sort() و هي تقوم بإعادة ترتيب عناصر القائمة ترتيبا أبجديا ، أنظروا نتيجة تنفيذ هذه الدالة على قائمتنا :
members.sort()
print(members)
['alaa', 'ali', 'khaled', 'leila', 'maryem', 'messaoud', 'mohammed', 'mohsen', 'mustapha', 'salem', 'semia']
المتوسط الحسابي:
هل لنا بإستراحة فنقوم بجولة في حقل الرياضيات ؟ ربما سيقول بعضكم أن هذا ليس بإستراحة بل "أشغالا شاقة"...على كل أستاذنا أكيد يعتذر ممن سيجدونه أشغالا شاقة و ليس إستراحة .
ما هو المتوسط الحسابي في الرياضيات ؟ 
لو لدينا عدد من الأرقام ، متوسطها الحسابي هو مجموع تلك الأرقام مقسوم على عدد تلك الأرقام !!سهلة أليس كذلك ؟ . 
إنتهى ال break و نعود لدرس البرمجة .
نعرّف متغير و نضع فيه عددا من الأرقام هكذا :
grates = [90,87,81,98,96,99]
نضع مجموع كل العناصر في المتغيّر s:
s = sum(grates)
print(s)
551
ثم نطلب من الدالة len أن تلتحق بنا على المنصة لأمر طارىء :
l = len(grates)
print(l)
6
ثم نعرّف المتوسط الحسابي avو نضع له معادلته الرياضية :
av = s/l
print(av)
91.83333333333333
و لتحسين مظهر ضيفنا av نقوم بتحديد عدد أرقامه العشرية هكذا :
av = round(av,1)
print(av)
91.8
و يمكننا مبدعنا البايثون من التعرّف على أدنى و أعلى قيمة ضمن عناصر القائمة بواسطة min و max هكذا :
print(min(grates))
81
print(max(grates))
99
يقول لنا البايثون أن القوائم تحتل مكان محدد في الذاكرة ، سنجري التجربة التالية :
ننشىء قائمة جديدة و نجعلها مساوية لقائمتنا grates و نطبع grates1
grates1 = grates
print(grates1)
[90, 87, 81, 98, 96, 99]
طبع لنا البايثون نفس القائمة و هذا أمر طبيعي .
ماذا لو حذفنا العنصر الأخير من القائمة grates1
grates1.pop(-1)
لنطبع القائمتين :
print(grates1)
print(grates)
[90, 87, 81, 98, 96]
[90, 87, 81, 98, 96]
أكيد أنكم لاحظتم أن البايثون حذف العنصر الأخير 99 من القائمتين ، بينما الأمر كان مختلفا مع السلاسل النصية .
توجد طريقتان للفصل بين القائمتين و التعامل مع واحدة بمعزل عن الأخرى :
grates2 = grates.copy()
grates3 = list(grates)
print("grates2 = ",grates2)
print("grates3 = ",grates3)
grates2 =  [90, 87, 81, 98, 96]
grates3 =  [90, 87, 81, 98, 96]
في هذه الحالات لو عدّلنا من grates2 و grates3 فإن هذا التعديل لن يمس grates بل ستظل كما هي دون تغيير .

الحلقات التكرارية for loops :
لو أردنا طباعة الأعداد من 1 الى 5 يمكننا تكرار أمر الطباعة 5 مرات كل مرة نطلب منه طباعة رقم من 1 الى 5 ، هذا ممكن عمليا و مقدور عليه ، ماذا أفعل لو احتجت لطباعة الأرقام من 1 الى 100 أو أكثر ؟ ماذا أفعل لو كتبنا برنامجا لمدرس ليقوم بواسطته ادخال بيانات طلبته و عددهم 60 مثلا ؟ هل نكتب أمر input() 60 مرة ؟ ماذا نفعل لو أردنا طباعة جدول الضرب للعدد 3 هل نكتب 3*1 = 3
3*2=6
3*3 = 9 
و هكذا لنفرض أننا نريد التوقف عند العدد 20 ...هل نكرر هذا الأمر 20 مرة ؟ 
طبعا الافتراضات السابقة هي معقولة و ممكنة و واقعية و لكن طريقة حلها هي التي لم تكن واقعية و لا علاقة لها بالبرمجة ، فالبرمجة جُعلت لتسهل حياة الناس و تسريع التعامل مع البيانات .
إذن الحل تعطينا إياه البرمجة و الحل هو الحلقات التكرارية التي سندرس منها اليوم for :
الحلقات التكرارية هي مفهوم برمجي يسمح لنا بتكرار عدد من الأكواد عدد معين من المرات وفقا لىليات يحددها المبرمج مثل وضع شرط معين  أو لعدد عناصر قائمة معينة أو غير ذلك .
مثال :
x = [1,2,3,4,5,6]
اذا أردنا معرفة مربع كل عنصر من عناصر هذه القائمة نكتب :
for i in x:
    print(i*i)
1
4
9
16
25
36

بعد معرفتنا بهذا المفهوم الرائع يمكننا الإستفادة منه في عدة حالات ، مثلا ، يمكننا المرور على كل عناصر القائمة xو فحصها عنصرا عنصرا لمعرفة الأعداد الزوجية و الأخرى الفردية .
كيف نعرف أن عددا ما هو زوجي ؟؟ علم الرياضيات يعرّف العدد الزوجي بأنه العدد الذي إذا قسمناه على 2 يكون باقي القسمة يساوي صفرا .
كل عدد باقي قسمته على 2 يساوي صفرا هو عدد زوجي و كل عدد يكون باقي قسمته على 2 ليس صفرا هو بالضرورة عددا فرديا .
كنا نتحدث بمقياس الرياضيات و ما رأيكم أن نعود للغوص في بحرنا و هو بحر البرمجة بالبايثون ؟.
العدد a نعرف أنه زوجي لو فحصناه بالطريقة التالية :
a% 2 = 0 
لو استجاب العدد aلهذه المعادلة فهو زوجي .
باقي القسمة في البايثون نرمز له بالرمز % .
نعود للحلقة التكرارية for :
for i in x :
    if i%2 == 0:
        print(i , "هو عدد زوجي")
    else:
        print(i,"هو عدد فردي")
و هذه هي النتيجة :
1 هو عدد فردي
2 هو عدد زوجي
3 هو عدد فردي
4 هو عدد زوجي
5 هو عدد فردي
6 هو عدد زوجي
الدالة range() تستخدم في البايثون عادة لترقيم شيء معين أي فحصه عدد من المرات و نضع بين قوسيها بداية العد و نهايته و أحيانا خطوة القفز ، مثال :
range(5) 
ستحسب من 0 الى 4 
أما range(6,21) فستحسب من 6 الى 20 و range(2,11,3) فستحسب من 2 الى 10 مع القفز ب 3 فتكون هكذا : 2 -5-8-.
هل مازلتم تذكرون قائمتنا members أستاذنا الجليل سليمان القسيمي قال لكم أنه يريد ترتيب هذه القائمة و إعطاء كل عنصر رقما تسلسليا و بالطبع هو الخبير بالبايثون يعلم جيدا أن هذا ممكن و هذا ما حدثكم عنه في محاضرته القيّمة :
for i in range(len(members)):
    print(f"{i+1}. {members[i]}")
و هذه النتيجة الرائعة :
1. alaa
2. ali
3. khaled
4. leila
5. maryem
6. messaoud
7. mohammed
8. mohsen
9. mustapha
10. salem
11. semia
و هل تذكرون فترة الإستراحة التي قضيناها مع المتوسط الحسابي ؟ 
سنعود لتلك القصة و لكن بطريقة مختلفة .
سننشىء قائمة فارغة و سنطلب من المستخدم ملؤها بأرقام كما يريد هو .
قبل إضافة كل رقم يدخله المستخدم للقائمة الفارغة نحوله الى رقم ( تذكير أن الدالة input ترجع لنا دوما string) .
ثم يضيفه البرنامج للقائمة .
و بعد أن ينتهي المستخدم من إدخال كل الأرقام المطلوبة منه ( عدد المرات محدد مسبقا ب range) يقوم البرنامج بحساب مجموع العناصر و يحتسب عدد العناصر يقوم بإحتساب المتوسط الحسابي . 
numbers = []
for i in range(5):
    number = input("أدخل رقما")
    number = int(number)
    numbers.append(number)
print(numbers)
av = sum(numbers)/len(numbers)
print(av)
و هذه هي النتيجة :
أدخل رقما2
أدخل رقما6
أدخل رقما12
أدخل رقما52
أدخل رقما36
[2, 6, 12, 52, 36]
21.6